<?php

class cachemgr {

    private static $_enable = false;

    private static $_co_depth = 0;

    private static $_cache_objects = array();

    private static $_instance = null;

    private static $_instance_name = null;

    private static $_cache_objects_exists = array();

    private static $_cache_check_version_key = '__ECOS_CACHEMGR_CACHE_CHECK_VERSION_KEY__';

    private static $_cache_check_version = null;

    private static $_cache_key_global_varys = null;

    private static $_vary_list_froce_mysql = false;

    private static $_cache_expirations = array();

    static public function init($with_cache = true) {
        if (! WITHOUT_CACHE && $with_cache && defined('CACHE_STORAGE') && constant('CACHE_STORAGE')) {
            self::$_instance_name = CACHE_STORAGE;
            self::$_enable = true;
        } else {
            self::$_instance_name = 'base_cache_nocache'; // todo：增加无cache类，提高无cache情况下程序的整体性能
            self::$_enable = false;
        }
        self::$_instance = null;
    }
 // End Function
    static public function enable() {
        return self::$_enable;
    }
 // End Function
    static public function instance() {
        if (is_null(self::$_instance)) {
            self::$_instance = kernel::single(self::$_instance_name);
        } // 使用实例时再构造实例
        return self::$_instance;
    }
 // End Function
    static public function get_modified($type, $vary_key) {
        return self::instance()->get_modified($type, $vary_key);
    }
 // End Function
    static public function set_modified($type, $vary_key, $time = 0) {
        self::store_vary_list(self::fetch_vary_list(true));
        return self::instance()->set_modified($type, $vary_key, $time);
    }
 // End Function
    static public function set_expiration($time) {
        self::update_expiration($time, self::$_co_depth);
    }
 // End Function
    static private function update_expiration($time, $level) {
        if ($time > 0) {
            if (! isset(self::$_cache_expirations[$level])) {
                self::$_cache_expirations[$level] = $time;
            } elseif (self::$_cache_expirations[$level] > $time) {
                self::$_cache_expirations[$level] = $time; // todo: 如果有更小的时间进来，则取用
            }
        }
    }
 // End Function
    static private function update_parents_expiration($time) {
        for ($level = self::$_co_depth; $level > 0; $level--) {
            self::update_expiration($time, $level);
        }
    }
 // End Function
    static public function get($key, &$return, $skipvary = false) {
        if (self::instance()->fetch(self::get_key($key), $data)) {
            if (($skipvary || $data['expires'] > 0) && time() > $data['expires']) {
                return false;
            } // todo:人工设置过期功能判断
            if (is_array($data['varys']) && ! $skipvary) {
                foreach ($data['varys'] as $type => $vary) {
                    foreach ($vary as $o) {
                        if (! is_array($data['cotime'][$type]) || ! array_key_exists($o, $data['cotime'][$type]) || $data['cotime'][$type][$o] != self::get_modified($type, $o)) {
                            return false;
                        } else {
                            $checks[$type][] = $o;
                        }
                    }
                }
            }
            $return = $data['content'];
            if (isset($checks)) {
                foreach ($checks as $type => $check) {
                    foreach ($check as $o) {
                        self::check_expires($type, $o);
                    }
                }
            } // 设置上级cache的check_expires
            return true;
        } else {
            return false;
        }
    }
 // End Function
    static public function set($key, $content, $params = array()) {
        $data = array(
            'content' => $content
        );
        if (is_array($params['varys'])) {
            $data['cotime'] = array();
            $data['varys'] = array();
            foreach ($params['varys'] as $type => $vary) {
                $type = strtoupper($type);
                foreach ($vary as $o => $val) {
                    $o = strtoupper($o);
                    $data['cotime'][$type][$o] = self::get_modified($type, $o);
                    $data['varys'][$type][] = $o;
                }
            }
        }
        $data['expires'] = ($params['expires'] > 0) ? $params['expires'] : 0; // todo: 设置过期时间
        if ($data['expires'] > 0) {
            self::update_parents_expiration($data['expires']);
        } // todo: 更新所有上级的过期时间
        return self::instance()->store(self::get_key($key), $data);
    }
 // End Function
    static public function exec($func, $args, $ttl = 3600) {
        if (is_array($func)) {
            $key = self::get_key('ECOS_CACHEMGR_PREFIX' . '_CLASS_' . get_class($func[0]) . '_FUNC_' . serialize($func[1]) . '_PARAMS_' . serialize($args));
        } else {
            $key = self::get_key('ECOS_CACHEMGR_PREFIX' . '_FUNC_' . $func . '_PARAMS_' . serialize($args));
        }
        
        if (self::instance()->fetch($key, $data) === false || (time() - $data['time'] > $ttl)) {
            $data['return'] = call_user_func_array($func, $args);
            $data['time'] = time();
            self::instance()->store($key, $data);
        }
        return $data['return'];
    }
 // End Function
    static public function get_key($key) {
        $key_array['key'] = $key;
        $key_array['version'] = self::get_cache_check_version();
        $key_array['global_varys'] = self::get_cache_global_varys();
        if (method_exists(self::instance(), "get_key")) {
            return self::instance()->get_key($key_array);
        } else {
            return md5(serialize($key_array));
        }
    }
 // End Function
    static public function get_cache_check_version() {
        if (! isset(self::$_cache_check_version)) {
            self::$_cache_check_version = self::ask_cache_check_version();
        } // 只取一次
        return self::$_cache_check_version;
    }
 // End Function
    static public function get_cache_global_varys() {
        // 引响全局的vary
        // todo：一般数据来源为get、post、cookie、session、server中取值或从http_refer等信息来判断取值
        // 保证global_varys的值不受程序改变而改变
        if (! isset(self::$_cache_key_global_varys)) {
            self::$_cache_key_global_varys = self::get_global_varys();
        } // 只取一次
        return self::$_cache_key_global_varys;
    }
 // End Function
    static public function get_global_varys() {
        $app_varys = array();
        $serviceList = kernel::serviceList('cachemgr_global_vary');
        foreach ($serviceList as $service) {
            $class_name = get_class($service);
            $p = strpos($class_name, '_');
            $varys = null;
            if (method_exists($service, 'get_varys')) {
                $varys = (array) $service->get_varys();
            }
            if (is_array($varys) && $p) {
                $app_id = substr($class_name, 0, $p);
                if (isset($app_varys[$app_id])) {
                    $app_varys[$app_id] = array_merge($app_varys[$app_id], $varys);
                } else {
                    $app_varys[$app_id] = $varys;
                }
                ksort($app_varys[$app_id]);
            }
        }
        ksort($app_varys);
        return $app_varys;
    }
 // End Function
    static public function ask_cache_check_version($force = false) {
        $key = self::get_cache_check_version_key();
        if ($force || self::enable()) {
            if (self::instance()->fetch($key, $val) === false) {
                $val = md5($key . time());
                self::instance()->store($key, $val);
                self::$_cache_check_version = $val; // todo：强制更新
            }
            return $val;
        } else {
            return 'static';
        }
    }
 // End Function
    static public function get_cache_check_version_key() {
        $kvprefix = (defined('KV_PREFIX')) ? KV_PREFIX : '';
        $key = md5($kvprefix . self::$_cache_check_version_key);
        return $key;
    }
 // End Function
    static public function co_start() {
        unset(self::$_cache_objects[++ self::$_co_depth]);
        unset(self::$_cache_objects_exists[self::$_co_depth]);
    }
 // End Function
    static public function co_end() {
        $data['expires'] = isset(self::$_cache_expirations[self::$_co_depth]) ? self::$_cache_expirations[self::$_co_depth] : null;
        $data['varys'] = self::$_cache_objects[self::$_co_depth--];
        return $data;
    }
 // End Function
    static public function check_expires($type, $cache_name) {
        $upper_type = strtoupper($type);
        for ($i = self::$_co_depth; $i > 0; $i--) {
            if (is_array($cache_name)) {
                foreach ($cache_name as $name) {
                    $upper_cache_name = strtoupper($name);
                    if ($upper_type != 'DB' || self::get_modified($type, $name) > 0) {
                        self::$_cache_objects[$i][$upper_type][$upper_cache_name] = 1;
                    }
                }
            } else {
                $upper_cache_name = strtoupper($cache_name);
                if ($upper_type != 'DB' || self::get_modified($type, $cache_name) > 0) {
                    self::$_cache_objects[$i][$upper_type][$upper_cache_name] = 1;
                }
            }
            self::$_cache_objects_exists[$i][$upper_type][strtoupper(md5(serialize($cache_name)))] = 1;
        }
    }
 // End Function
    static public function check_current_co_depth() {
        return self::$_co_depth;
    }
 // End Function
    static public function check_current_co_objects_exists($type, $cache_name) {
        return isset(self::$_cache_objects_exists[self::$_co_depth][strtoupper($type)][strtoupper(md5(serialize($cache_name)))]);
    }
 // End Function
    static public function store_vary_list($vary_list) {
        return base_kvstore::instance('cache/expires')->store('vary_list', $vary_list);
    }
 // End Function
    static public function fetch_vary_list($force = false) {
        $vary_list = array();
        if (self::$_vary_list_froce_mysql === true || $force === true) {
            $rows = kernel::database()->select('SELECT UPPER(`type`) AS `type`, UPPER(`name`) AS `name`, `expire` FROM sdb_base_cache_expires', true);
            if (defined('BASE_CACHE_EXPIRES')) {
                foreach ($rows as $row) {
                    base_kvstore::instance('cache/cache_expires')->fetch($row['name'], $result);
                    if (empty($result)) {
                        base_kvstore::instance('cache/cache_expires')->store($row['name'], $row);
                        $vary_list[$row['type']][$row['name']] = $row['expire'];
                    } else {
                        $vary_list[$result['type']][$result['name']] = $result['expire'];
                    }
                }
            } else {
                foreach ($rows as $row) {
                    $vary_list[$row['type']][$row['name']] = $row['expire'];
                }
            }
        } else {
            base_kvstore::instance('cache/expires')->fetch('vary_list', $vary_list);
            if (empty($vary_list)) {
                // 如果发生意外，取出数据为空，则再取次数据库数据并写回kvstore
                $vary_list = self::fetch_vary_list(true);
                // self::store_vary_list($vary_list);
            }
        }
        return $vary_list;
    }
 // End Function
    static public function status(&$msg) {
        if (method_exists(self::instance(), "status")) {
            $msg = self::instance()->status();
            return true;
        } else {
            $msg = app::get('base')->_('当前缓存控制器无法显示状态');
            return false;
        }
    }
 // End Function
    static public function optimize(&$msg) {
        if (method_exists(self::instance(), "optimize")) {
            return self::instance()->optimize();
        } else {
            $msg = app::get('base')->_('当前缓存控制器无需优化');
            return false;
        }
    }
 // End Function
    static public function clean(&$msg) {
        if (method_exists(self::instance(), "clean")) {
            $res = self::instance()->clean();
        } else {
            $res = self::ask_cache_check_version(true);
        }
        if ($res) {
            foreach (kernel::servicelist('base_cachemgr_clean') as $service) {
                if (is_object($service) && method_exists($service, 'clean')) {
                    $service->clean();
                }
            }
            return true;
        } else {
            return false;
        }
    } // End Function
}//End Class
